﻿using System;

namespace Microsoft.Xna.Framework
{
    /// <summary>
    /// 	Base class for all XNA Framework game components.
    /// </summary>
    public class GameComponent : IGameComponent, IUpdateable, IDisposable
    {
        #region Fields & Properties

        private readonly Game _game;

        /// <summary>
        /// 	Gets the Game associated with this GameComponent.
        /// </summary>
        public Game Game
        {
            get
            {
                return _game;
            }
        }

        #endregion

        #region Constructors & Destructor

        /// <summary>
        /// 	Initializes a new instance of this class.
        /// </summary>
        /// <param name = "game">Game that the game component should be attached to.</param>
        public GameComponent(Game game)
        {
            _enabled = true;
            _game = game;
        }

        /// <summary>
        /// 	Allows a GameComponent to attempt to free resources and perform other cleanup operations before garbage collection reclaims the GameComponent.
        /// </summary>
        ~GameComponent()
        {
            Dispose(false);
        }

        #endregion

        #region Implementation of IGameComponent

        /// <summary>
        /// 	Called when the component should be initialized. 
        /// 	This method can be used for tasks like querying for services the component needs and setting up non-graphics resources.
        /// </summary>
        public virtual void Initialize()
        {
        }

        #endregion

        #region Implementation of IUpdateable

        #region Enabled

        private bool _enabled;

        /// <summary>
        /// 	Indicates whether GameComponent.Update should be called when Game.Update is called.
        /// </summary>
        public bool Enabled
        {
            get
            {
                return _enabled;
            }
            set
            {
                if (_enabled != value)
                {
                    _enabled = value;
                    OnEnabledChanged(EventArgs.Empty);
                }
            }
        }

        public event EventHandler<EventArgs> EnabledChanged;

        /// <summary>
        /// 	Called when the Enabled property changes. Raises the EnabledChanged event.
        /// </summary>
        /// <param name = "args">Arguments to the EnabledChanged event.</param>
        protected virtual void OnEnabledChanged(EventArgs args)
        {
            var onEnabledChanged = EnabledChanged;
            if (onEnabledChanged != null)
            {
                onEnabledChanged(this, args);
            }
        }

        #endregion

        #region UpdateOrder

        private int _updateOrder;

        /// <summary>
        /// 	Indicates the order in which the GameComponent should be updated relative to other GameComponent instances. Lower values are updated first.
        /// </summary>
        public int UpdateOrder
        {
            get
            {
                return _updateOrder;
            }
            set
            {
                if (_updateOrder != value)
                {
                    _updateOrder = value;
                    OnUpdateOrderChanged(EventArgs.Empty);
                }
            }
        }

        public event EventHandler<EventArgs> UpdateOrderChanged;

        /// <summary>
        /// 	Called when the UpdateOrder property changes. Raises the UpdateOrderChanged event.
        /// </summary>
        /// <param name = "args">Arguments to the UpdateOrderChanged event.</param>
        protected virtual void OnUpdateOrderChanged(EventArgs args)
        {
            var onUpdateOrderChanged = UpdateOrderChanged;
            if (onUpdateOrderChanged != null)
            {
                onUpdateOrderChanged(this, args);
            }
        }

        #endregion

        /// <summary>
        /// 	Called when the GameComponent needs to be updated. Override this method with component-specific update code.
        /// </summary>
        /// <param name = "gameTime">Time elapsed since the last call to Update</param>
        public virtual void Update(GameTime gameTime)
        {
        }

        #endregion

        #region Implementation of IDisposable

        #region Dispose

        /// <summary>
        /// 	Releases the unmanaged resources used by the GameComponent and optionally releases the managed resources.
        /// </summary>
        /// <param name = "disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                lock (this)
                {
                    if (_game != null)
                    {
                        _game.Components.Remove(this);
                    }
                    OnDisposed(EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// 	Raised when the GameComponent is disposed.
        /// </summary>
        public event EventHandler<EventArgs> Disposed;

        /// <summary>
        /// 	Called when the Dispose method is invoked. Raises the Disposed event.
        /// </summary>
        /// <param name = "args">Arguments to the Disposed event.</param>
        private void OnDisposed(EventArgs args)
        {
            var onDisposed = Disposed;
            if (onDisposed != null)
            {
                onDisposed(this, args);
            }
        }

        #endregion

        /// <summary>
        /// 	Immediately releases the unmanaged resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}